home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 14 - Glypha III / Glypha III source / GlyphaIII Code ƒ / Play.c < prev    next >
Text File  |  1995-03-29  |  44KB  |  1,189 lines

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Play.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This (rather large) file handles all player routines while a game is in…
  9. // progress. It gets the player's input, moves the player, tests for collisions…
  10. // and generally handles the "main game loop".  Enemies and actually drawing…
  11. // the graphics to the screen are handled in other files.
  12.  
  13. #include "Externs.h"
  14.  
  15.  
  16. #define kFlapImpulse            48
  17. #define kGlideImpulse            12
  18. #define kAirResistance            2
  19. #define kMaxHVelocity            192
  20. #define kMaxVVelocity            512
  21. #define kNumLightningStrikes    5
  22.  
  23.  
  24. void SetUpLevel (void);
  25. void ResetPlayer (Boolean);
  26. void OffAMortal (void);
  27. void DoCommandKey (void);
  28. void GetPlayerInput (void);
  29. void HandlePlayerIdle (void);
  30. void HandlePlayerFlying (void);
  31. void HandlePlayerWalking (void);
  32. void HandlePlayerSinking (void);
  33. void HandlePlayerFalling (void);
  34. void HandlePlayerBones (void);
  35. void MovePlayer (void);
  36. void CheckTouchDownCollision (void);
  37. void CheckPlatformCollision (void);
  38. void KeepPlayerOnPlatform (void);
  39. void CheckLavaRoofCollision (void);
  40. void SetAndCheckPlayerDest (void);
  41. void HandleLightning (void);
  42. void FinishLightning (void);
  43. void HandleCountDownTimer (void);
  44. void CheckHighScore (void);
  45.  
  46.  
  47. playerType    thePlayer;
  48. enemyType    theEnemies[kMaxEnemies];
  49. KeyMap        theKeys;
  50. Rect        platformRects[6], touchDownRects[6], enemyRects[24];
  51. Rect        enemyInitRects[5];
  52. long        theScore, wasTensOfThousands;
  53. short        numLedges, beginOnLevel, levelOn, livesLeft, lightH, lightV;
  54. short        lightningCount, numEnemies, countDownTimer;
  55. Boolean        playing, pausing, flapKeyDown, evenFrame;
  56. Boolean        doEnemyFlapSound, doEnemyScrapeSound;
  57.  
  58. extern    handInfo    theHand;
  59. extern    prefsInfo    thePrefs;
  60. extern    Rect        playerRects[11], mainWindowRect;
  61. extern    short        numUpdateRects1, numUpdateRects2, numOwls;
  62. extern    Boolean        quitting, openTheScores;
  63.  
  64.  
  65. //==============================================================  Functions
  66. //--------------------------------------------------------------  InitNewGame
  67.  
  68. // This funciton sets up variables and readies for a new game.  It is called…
  69. // only when a the user selects "New Game" - during the course of a game, it…
  70. // is  not called again.
  71.  
  72. void InitNewGame (void)
  73. {                                // Initialize a number of game variables.
  74.     countDownTimer = 0;            // Zero count down timer.
  75.     numLedges = 3;                // Initial number of ledges (platforms).
  76.     beginOnLevel = 1;            // Ledge (platform) the player is on (center ledge).
  77.     levelOn = 0;                // Game level on (first level).
  78.     livesLeft = kInitNumLives;    // Number of player lives remaining.
  79.     theScore = 0L;                // Player's score (a long - can go to 2 billion).
  80.     playing = TRUE;                // Flag playing.
  81.     pausing = FALSE;            // Not paused.
  82.     evenFrame = TRUE;            // Set an initial state for evenFrame.
  83.     wasTensOfThousands = 0L;    // Used for noting when player gets an extra life.
  84.     numOwls = 4;                // Number of "owl" enemies for this level.
  85.     
  86.     numUpdateRects1 = 0;        // Init number of "update" rectangles.
  87.     numUpdateRects2 = 0;        // (see Render.c to see what these do)
  88.     
  89.     InitHandLocation();            // Get the mummy hand down in the lava.
  90.     theHand.mode = kLurking;    // Flag the hand in "lurking" mode.
  91.     
  92.     SetUpLevel();                // Set up platforms for first level (wave).
  93.     
  94.     DumpBackToWorkMap();        // Copy background offscreen to "work" offscreen.
  95.     
  96.     UpdateLivesNumbers();        // Display number of lives remaining on screen.
  97.     UpdateScoreNumbers();        // Display the player's score (zero at this point).
  98.     UpdateLevelNumbers();        // Display the level (wave) the player is on.
  99.     
  100.     GenerateEnemies();            // Prepare all enemies for this level.
  101.     ResetPlayer(TRUE);            // Initialize all player variables and put on ledge.
  102. }
  103.  
  104. //--------------------------------------------------------------  SetUpLevel
  105.  
  106. // Primarily, this function is called to set up the ledges for the…
  107. // current level (wave) the player is on.  It determines how many…
  108. // are required and then draws these offscreen.  It also flashes…
  109. // the obelisks and strikes the lightning.
  110.  
  111. void SetUpLevel (void)
  112. {
  113.     short        wasLedges, waveMultiple;
  114.     
  115.     KillOffEye();                    // Return eye to the aether.
  116.     
  117.     wasLedges = numLedges;            // Remember number of ledges.
  118.     waveMultiple = levelOn % 5;        // Waves repeat every 5th wave (but harder!).
  119.     
  120.     switch (waveMultiple)            // See which of the 5 we're on.
  121.     {
  122.         case 0:                        // Waves 0, 5, 10, …
  123.         numLedges = 5;                // have 5 ledges (platforms) on screen.
  124.         break;
  125.         
  126.         case 1:                        // Waves 1, 6, 11, …
  127.         numLedges = 6;                // are up to 6 ledges (platforms) on screen.
  128.         break;
  129.         
  130.         case 2:                        // Waves 2, 7, 12, …
  131.         numLedges = 5;                // return to 5 ledges (platforms) on screen.
  132.         break;
  133.         
  134.         case 3:                        // Waves 3, 8, 13, …
  135.         numLedges = 3;                // drop to 3 ledges (platforms) on screen.
  136.         break;
  137.         
  138.         case 4:                        // Waves 4, 9, 14, …
  139.         numLedges = 6;                // and return to 6 ledges (platforms) on screen.
  140.         break;
  141.     }
  142.     
  143.     if (wasLedges != numLedges)        // No need to redraw if platforms are unchanged.
  144.         DrawPlatforms(numLedges);
  145.     
  146.     FlashObelisks(TRUE);            // Flash the obelisks.
  147.     
  148.     GenerateLightning(320, 429);    // Lightning strikes platform 0.
  149.     StrikeLightning();
  150.     LogNextTick(2);
  151.     WaitForNextTick();
  152.     StrikeLightning();
  153.     
  154.     GenerateLightning(95, 289);        // Lightning strikes platform 1.
  155.     StrikeLightning();
  156.     LogNextTick(2);
  157.     WaitForNextTick();
  158.     StrikeLightning();
  159.     
  160.     GenerateLightning(95, 110);        // Lightning strikes platform 3.
  161.     StrikeLightning();
  162.     LogNextTick(2);
  163.     WaitForNextTick();
  164.     StrikeLightning();
  165.     
  166.     GenerateLightning(320, 195);    // Lightning strikes platform 5.
  167.     StrikeLightning();
  168.     LogNextTick(2);
  169.     WaitForNextTick();
  170.     StrikeLightning();
  171.     
  172.     GenerateLightning(545, 110);    // Lightning strikes platform 4.
  173.     StrikeLightning();
  174.     LogNextTick(2);
  175.     WaitForNextTick();
  176.     StrikeLightning();
  177.     
  178.     GenerateLightning(545, 289);    // Lightning strikes platform 2.
  179.     StrikeLightning();
  180.     LogNextTick(2);
  181.     WaitForNextTick();
  182.     StrikeLightning();
  183.     
  184.     FlashObelisks(FALSE);            // "Unflash" obelisks (return to normal state).
  185.                                     // Play lightning sound!
  186.     PlayExternalSound(kLightningSound, kLightningPriority);
  187.     
  188.     UpdateLevelNumbers();            // Display the current level on screen.
  189. }
  190.  
  191. //--------------------------------------------------------------  ResetPlayer
  192.  
  193. // This function prepares the player - it places the player and his/her mount…
  194. // in their proper starting location (depending on which platform they are to…
  195. // begin on), and it sets all the player's variables to their initial state.
  196.  
  197. void ResetPlayer (Boolean initialPlace)
  198. {
  199.     short        location;
  200.     
  201.     thePlayer.srcNum = 5;            // Set which graphic (frame) the player is to use.
  202.     thePlayer.frame = 320;            // This variable will be used as a coutndown timer.
  203.     
  204.     if (initialPlace)                // If "initialPlace" is TRUE, …
  205.         location = 0;                // the player is to begin on the lowest platform.
  206.     else                            // Otherwise, a random location is chosen.
  207.         location = RandomInt(numLedges);
  208.     
  209.     switch (location)                // Move player horizontally and vertically to their…
  210.     {                                // proper location (based on ledge # they're on).
  211.         case 0:
  212.         thePlayer.h = 296 << 4;        // Bottom center ledge.
  213.         thePlayer.v = 377 << 4;        // We're scaling by 16.
  214.         break;
  215.         
  216.         case 1:
  217.         thePlayer.h = 102 << 4;        // Lower left ledge.
  218.         thePlayer.v = 237 << 4;
  219.         break;
  220.         
  221.         case 2:
  222.         thePlayer.h = 489 << 4;        // Lower right ledge.
  223.         thePlayer.v = 237 << 4;
  224.         break;
  225.         
  226.         case 3:
  227.         thePlayer.h = 102 << 4;        // Top left ledge.
  228.         thePlayer.v = 58 << 4;
  229.         break;
  230.         
  231.         case 4:
  232.         thePlayer.h = 489 << 4;        // Top right ledge.
  233.         thePlayer.v = 58 << 4;
  234.         break;
  235.         
  236.         case 5:
  237.         thePlayer.h = 296 << 4;        // Top central ledge.
  238.         thePlayer.v = 143 << 4;
  239.         break;
  240.     }
  241.                                     // Assign destination rectangle.
  242.     thePlayer.dest = playerRects[thePlayer.srcNum];
  243.     ZeroRectCorner(&thePlayer.dest);
  244.     OffsetRect(&thePlayer.dest, thePlayer.h >> 4, thePlayer.v >> 4);
  245.     thePlayer.wasDest = thePlayer.dest;
  246.     
  247.     thePlayer.hVel = 0;                // Player initially has no velocity.
  248.     thePlayer.vVel = 0;
  249.     thePlayer.facingRight = TRUE;    // We're facing to the right.
  250.     thePlayer.flapping = FALSE;        // We're not flapping our wings initially.
  251.     thePlayer.wrapping = FALSE;        // We can't be wrapping around the edge of the screen.
  252.     thePlayer.clutched = FALSE;        // The hand ain't got us.
  253.     thePlayer.mode = kIdle;            // Our mode is "idle" - waiting to be "born".
  254.     if (lightningCount == 0)        // Prepare for a lightning display to "birth" us.
  255.     {
  256.         lightH = thePlayer.dest.left + 24;
  257.         lightV = thePlayer.dest.bottom - 24;
  258.         lightningCount = kNumLightningStrikes;
  259.     }
  260. }
  261.  
  262. //--------------------------------------------------------------  OffAMortal
  263.  
  264. // Alas, 'tis here that a player is brought who loses a life.
  265.  
  266. void OffAMortal (void)
  267. {
  268.     livesLeft--;                // Decrememnt number of player lives left.
  269.     
  270.     if (livesLeft > 0)            // Indeed, are there lives remaining?
  271.     {
  272.         ResetPlayer(FALSE);        // Good, start a new one off.
  273.         UpdateLivesNumbers();    // Make note of the number of lives remaining.
  274.     }
  275.     else                        // Otherwise, we are at the dreaded "Game Over".
  276.         playing = FALSE;        // Set flag to drop us out of game loop.
  277. }
  278.  
  279. //--------------------------------------------------------------  DoCommandKey
  280.  
  281. // This function handles the case when the user has held down the command…
  282. // key.  Note, this only applies to input when a game is in session - otherwise…
  283. // a standard event loop handles command keys and everything else.
  284.  
  285. void DoCommandKey (void)
  286. {
  287.     if (BitTst(&theKeys, kEKeyMap))            // Test for "command - E"…
  288.     {
  289.         playing = FALSE;                    // which would indicate "End Game".
  290.     }
  291.     else if (BitTst(&theKeys, kPKeyMap))    // Otherwise, see if it's "command - P".
  292.     {
  293.         pausing = TRUE;                        // This means the player is pausing the game.
  294.         MenusReflectMode();                    // Gray-out menus etc.
  295.         DumpMainToWorkMap();                // Save screen to offscreen.
  296.     }
  297.     else if (BitTst(&theKeys, kQKeyMap))    // Or perhaps the player hit "command - Q".
  298.     {
  299.         playing = FALSE;                    // Set flag to drop out of game loop.
  300.         quitting = TRUE;                    // Set flag to drop out of Glypha.
  301.     }
  302. }
  303.  
  304. //--------------------------------------------------------------  GetPlayerInput
  305.  
  306. // This function looks for keystrokes when a game is underway.  We don't use…
  307. // the more conventional event routines (like GetNextEvent()), because they're…
  308. // notoriously slow, allow background tasks, introduce possible INIT problems,…
  309. // and we don't have to.  Instead, we'll rely on GetKeys() (which has its own…
  310. // set of problems - but we deal with them).
  311.  
  312. void GetPlayerInput (void)
  313. {
  314.     thePlayer.flapping = FALSE;                // Assume we're not flapping.
  315.     thePlayer.walking = FALSE;                // Assume too we're not walking.
  316.     
  317.     GetKeys(theKeys);                        // Get the current keyboard keymap.
  318.     if (BitTst(&theKeys, kCommandKeyMap))    // See first if command key down…
  319.         DoCommandKey();                        // and handle those seperately.
  320.     else                                    // If not command key, continue.
  321.     {                                        // Look for one of the two "flap" keys.
  322.         if ((BitTst(&theKeys, kSpaceBarMap)) || (BitTst(&theKeys, kDownArrowKeyMap)))
  323.         {
  324.             if (thePlayer.mode == kIdle)    // Handle special case when player is idle.
  325.             {
  326.                 thePlayer.mode = kWalking;    // Set the player's mode now to walking.
  327.                 thePlayer.frame = 0;        // Used to note "state" of walking.
  328.             }                                // Otherwise, if player is flying or walking…
  329.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  330.             {
  331.                 if (!flapKeyDown)            // If flap key was not down last frame…
  332.                 {                            // (this is to prevent "automatic fire").
  333.                                             // Give player lift.
  334.                     thePlayer.vVel -= kFlapImpulse;
  335.                     flapKeyDown = TRUE;        // Note that the flap key is down.
  336.                                             // Play the "flap" sound.
  337.                     PlayExternalSound(kFlapSound, kFlapPriority);
  338.                                             // Set player flag to indicate flapping.
  339.                     thePlayer.flapping = TRUE;
  340.                 }
  341.             }
  342.         }
  343.         else
  344.             flapKeyDown = FALSE;            // If flap key not down, remember this.
  345.         
  346.                                             // Test now for one of three "right" keys.
  347.         if ((BitTst(&theKeys, kRightArrowKeyMap) || 
  348.                 BitTst(&theKeys, kSKeyMap) || 
  349.                 BitTst(&theKeys, kQuoteMap)) && 
  350.                 (thePlayer.hVel < kMaxHVelocity))
  351.         {
  352.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  353.             {                                // They are to begin walking (no longer idle).
  354.                 thePlayer.mode = kWalking;
  355.                 thePlayer.frame = 0;
  356.             }
  357.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  358.             {                                // If flying or walking, player moves right.
  359.                 if (!thePlayer.facingRight)    // If facing left, player does an about face.
  360.                 {
  361.                     thePlayer.facingRight = TRUE;
  362.                     if (thePlayer.clutched)
  363.                     {
  364.                         thePlayer.dest.left += 18;
  365.                         thePlayer.dest.right += 18;                        
  366.                         thePlayer.h = thePlayer.dest.left << 4;
  367.                         thePlayer.wasH = thePlayer.h;
  368.                         thePlayer.wasDest = thePlayer.dest;
  369.                     }
  370.                 }                            // Otherwise, if facing right already…
  371.                 else
  372.                 {                            // If flying, add to their horizontal velocity.
  373.                     if (thePlayer.mode == kFlying)
  374.                         thePlayer.hVel += kGlideImpulse;
  375.                     else                    // If walking, set flag to indicate a step.
  376.                         thePlayer.walking = TRUE;
  377.                 }
  378.             }
  379.         }                                    // Test now for one of three "left" keys.
  380.         else if ((BitTst(&theKeys, kLeftArrowKeyMap) || 
  381.                 BitTst(&theKeys, kAKeyMap) || 
  382.                 BitTst(&theKeys, kColonMap)) && 
  383.                 (thePlayer.hVel > -kMaxHVelocity))
  384.         {
  385.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  386.             {
  387.                 thePlayer.mode = kWalking;
  388.                 thePlayer.frame = 0;
  389.             }
  390.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  391.             {                                // If flying or walking, player moves left.
  392.                 if (thePlayer.facingRight)    // If facing right, player does an about face.
  393.                 {                            // Flag player facing left.
  394.                     thePlayer.facingRight = FALSE;
  395.                     if (thePlayer.clutched)    // Handle case where player gripped by hand.
  396.                     {                        // An about face handled a bit differently.
  397.                         thePlayer.dest.left -= 18;
  398.                         thePlayer.dest.right -= 18;
  399.                         thePlayer.h = thePlayer.dest.left << 4;
  400.                         thePlayer.wasH = thePlayer.h;
  401.                         thePlayer.wasDest = thePlayer.dest;
  402.                     }
  403.                 }
  404.                 else                        // Otherwise, player already facing left.
  405.                 {                            // So player will move left.
  406.                     if (thePlayer.mode == kFlying)
  407.                         thePlayer.hVel -= kGlideImpulse;
  408.                     else
  409.                         thePlayer.walking = TRUE;
  410.                 }
  411.             }
  412.         }
  413.     }
  414. }
  415.  
  416. //--------------------------------------------------------------  HandlePlayerIdle
  417.  
  418. // Following are a number of functions handling the player's different "modes".
  419. // This first function handles the player when in "idle" mode.  When idle, the…
  420. // player is standing on a platform - having just been "born".  This is when the…
  421. // player is in a "safe" mode - meaning no enemy can kill them.  The player remains…
  422. // in idle mode until they hit a key to flap or move or until a timer (thePlayer.frame)…
  423. // counts down to zero.
  424.  
  425. void HandlePlayerIdle (void)
  426. {
  427.     thePlayer.frame--;                // Count down the timer.
  428.     if (thePlayer.frame == 0)        // See if timer has reached zero yet.
  429.         thePlayer.mode = kWalking;    // If so, player is no longer idle.
  430.     
  431.     SetAndCheckPlayerDest();        // Keep player on platform.
  432. }
  433.  
  434. //--------------------------------------------------------------  HandlePlayerFlying
  435.  
  436. // This function handles a player in "flying" mode.  In flying mode, the player…
  437. // is alive and not standing/walking on any platform.  A plyaer remains in flying…
  438. // mode until the player dies (collides unfavorably with an enemy), is caught by…
  439. // the hand, or comes near the top of a platform (in which case they land and…
  440. // switch to walking mode).  While in flying mode, gravity pulls the player down…
  441. // while friction acts to slow the player down.
  442.  
  443. void HandlePlayerFlying (void)
  444. {    
  445.     if (thePlayer.hVel > 0)                    // If player has a positive hori. velocity…
  446.     {                                        // subtract frictional constant from velocity.
  447.         thePlayer.hVel -= kAirResistance;
  448.         if (thePlayer.hVel < 0)                // Don't let it go negative (otherwise, you…
  449.             thePlayer.hVel = 0;                // can get a "yo-yo" effect set up).
  450.     }
  451.     else if (thePlayer.hVel < 0)            // Otherwise, if horizontal velocity negative…
  452.     {                                        // add firctional constant to hori. velocity.
  453.         thePlayer.hVel += kAirResistance;
  454.         if (thePlayer.hVel > 0)
  455.             thePlayer.hVel = 0;
  456.     }
  457.     
  458.     thePlayer.vVel += kGravity;                // Add gravity to player's vertical velocity.
  459.     
  460.     if (thePlayer.vVel > kMaxVVelocity)        // Don't allow player to fall too fast.
  461.         thePlayer.vVel = kMaxVVelocity;
  462.     else if (thePlayer.vVel < -kMaxVVelocity)
  463.         thePlayer.vVel = -kMaxVVelocity;    // And don't allow player to climb too fast.
  464.     
  465.     thePlayer.h += thePlayer.hVel;            // Add velocities to players position.
  466.     thePlayer.v += thePlayer.vVel;
  467.                                             // Now we determine which graphic to use.
  468.     if (thePlayer.facingRight)                // There are the set of right-facing graphics.
  469.     {
  470.         thePlayer.srcNum = 1;                // Assume standard right-facing graphic.
  471.         if (thePlayer.vVel < -kDontFlapVel)    // Now we jump through a series of hoops…
  472.         {                                    // simply to determine whether we'll use…
  473.             if (thePlayer.flapping)            // the graphic of the player with the wings…
  474.                 thePlayer.srcNum = 0;        // up (srcNum = 0) or with the wings down…
  475.             else                            // (srcNum = 1).
  476.                 thePlayer.srcNum = 1;
  477.         }
  478.         else if (thePlayer.vVel > kDontFlapVel)
  479.         {
  480.             if (thePlayer.flapping)
  481.                 thePlayer.srcNum = 1;
  482.             else
  483.                 thePlayer.srcNum = 0;
  484.         }
  485.         else if (thePlayer.flapping)
  486.             thePlayer.srcNum = 0;
  487.     }
  488.     else                                    // If the player is facing left…
  489.     {                                        // We jump through a similar set of hoops…
  490.         thePlayer.srcNum = 2;                // this time choosing between srcNum = 2 …
  491.         if (thePlayer.vVel < -kDontFlapVel)    // and srcNum = 3.
  492.         {
  493.             if (thePlayer.flapping)
  494.                 thePlayer.srcNum = 3;
  495.             else
  496.                 thePlayer.srcNum = 2;
  497.         }
  498.         else if (thePlayer.vVel > kDontFlapVel)
  499.         {
  500.             if (thePlayer.flapping)
  501.                 thePlayer.srcNum = 2;
  502.             else
  503.                 thePlayer.srcNum = 3;
  504.         }
  505.         else if (thePlayer.flapping)
  506.             thePlayer.srcNum = 3;
  507.     }
  508.     
  509.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  510.     
  511.     CheckLavaRoofCollision();                // See if player hit top or bottom of screen.
  512.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  513.     CheckPlatformCollision();                // See if player collided with platform.
  514.     CheckTouchDownCollision();                // See if player has landed on platform.
  515. }
  516.  
  517. //--------------------------------------------------------------  HandlePlayerWalking
  518.  
  519. // This function handles a player in "walking" mode.  They remain in this mode…
  520. // until they walk off a platform's edge, flap to lift off the platform, or…
  521. // collide unfavorably with an enemy (die).  While in walking mode, we need only…
  522. // determine which frame of animation to display (if the player is taking steps)…
  523. // and check for the usual set of collisions.
  524.  
  525. void HandlePlayerWalking (void)
  526. {
  527.     short        desiredHVel;
  528.     
  529.     if (thePlayer.walking)                    // This means user is actively holding down…
  530.     {                                        // the left or right key.
  531.         if (evenFrame)                        // Now we jump through a number of hoops…
  532.         {                                    // in order to get a semi-realistic…
  533.             if (thePlayer.facingRight)        // "stepping" animation going.  We take steps…
  534.             {                                // only on "even frames".
  535.                 if (thePlayer.srcNum == 4)
  536.                     desiredHVel = 208;
  537.                 else
  538.                     desiredHVel = 128;
  539.             }
  540.             else
  541.             {
  542.                 if (thePlayer.srcNum == 7)
  543.                     desiredHVel = -208;
  544.                 else
  545.                     desiredHVel = -128;
  546.             }
  547.             
  548.             if (thePlayer.hVel < desiredHVel)
  549.             {
  550.                 thePlayer.hVel += 80;        // Move player right.
  551.                 if (thePlayer.hVel > desiredHVel)
  552.                 {                            // This is the case where player is walking.
  553.                     thePlayer.hVel = desiredHVel;
  554.                     PlayExternalSound(kWalkSound, kWalkPriority);
  555.                 }
  556.                 else                        // In this case, player is skidding.
  557.                     PlayExternalSound(kScreechSound, kScreechPriority);
  558.             }
  559.             else
  560.             {
  561.                 thePlayer.hVel -= 80;        // Move player to the left.
  562.                 if (thePlayer.hVel < desiredHVel)
  563.                 {                            // Player is stepping to left.
  564.                     thePlayer.hVel = desiredHVel;
  565.                     PlayExternalSound(kWalkSound, kWalkPriority);
  566.                 }
  567.                 else                        // Player is skidding to a stop.
  568.                     PlayExternalSound(kScreechSound, kScreechPriority);
  569.             }
  570.         }
  571.     }
  572.     else                                    // If user is not actively holding down the…
  573.     {                                        // left or right key, bring player to a stop.
  574.         thePlayer.hVel -= thePlayer.hVel / 4;
  575.         if ((thePlayer.hVel < 4) && (thePlayer.hVel > -4))
  576.             thePlayer.hVel = 0;                // If close to zero (within 4), stop player.
  577.         else                                // Othewrwise, play the skidding sound.
  578.             PlayExternalSound(kScreechSound, kScreechPriority);
  579.     }
  580.     
  581.     if (thePlayer.vVel > kMaxVVelocity)        // Keep player from moving too quickly…
  582.         thePlayer.vVel = kMaxVVelocity;        // left or right.
  583.     else if (thePlayer.vVel < -kMaxVVelocity)
  584.         thePlayer.vVel = -kMaxVVelocity;
  585.     
  586.     thePlayer.h += thePlayer.hVel;            // Move player horizontally and vertically…
  587.     thePlayer.v += thePlayer.vVel;            // by the corresponding velocity.
  588.     
  589.     if (thePlayer.walking)                    // "If player holding down left or right keys…".
  590.     {
  591.         if (evenFrame)                        // Here's where we toggle between the two…
  592.         {                                    // frames of "stepping" animation.
  593.             if (thePlayer.facingRight)
  594.                 thePlayer.srcNum = 9 - thePlayer.srcNum;
  595.             else
  596.                 thePlayer.srcNum = 13 - thePlayer.srcNum;
  597.         }
  598.     }
  599.     else                                    // If the player not holding down keys…
  600.     {                                        // draw the player just standing there.
  601.         if (thePlayer.facingRight)
  602.             thePlayer.srcNum = 5;
  603.         else
  604.             thePlayer.srcNum = 6;
  605.     }
  606.     
  607.     SetAndCheckPlayerDest();                // Check for wrap-around and all that.
  608.     
  609.     CheckTouchDownCollision();                // See if player still on platform.
  610.     KeepPlayerOnPlatform();                    // Don't let player "sink through" ledge.
  611.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  612. }
  613.  
  614. //--------------------------------------------------------------  HandlePlayerSinking
  615.  
  616. // When the player is in "sinking" mode, they are on a one-way ticket to death.
  617. // The player is sinking into the lava.  We put the player into this mode (rather…
  618. // than kill them outright) so that we can have a number of frames of them slowly…
  619. // slipping beneath the surface of the lava.  When the get below the surface of…
  620. // the lava, they will be officially "killed" and a new player will be "born",
  621.  
  622. void HandlePlayerSinking (void)
  623. {
  624.     thePlayer.hVel = 0;                        // Don't allow horizontal motion.
  625.     thePlayer.vVel = 16;                    // They will sink at this constant rate.
  626.     if (thePlayer.dest.top > kLavaHeight)    // See if they slipped below the surface.
  627.         OffAMortal();                        // If they did, kill 'em.
  628.     
  629.     thePlayer.v += thePlayer.vVel;            // Otherwise, move them down a notch.
  630.     
  631.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  632. }
  633.  
  634. //--------------------------------------------------------------  HandlePlayerFalling
  635.  
  636. // "Falling" refers to a player who is dead already but is still careening…
  637. // down the screen as a skeleton.  If (when) the player lands on a ledge they…
  638. // will turn into a pile of bones for a short duration.  If instead they fall…
  639. // into the lava, they'll sink.  In any event, it is then that they are…
  640. // officially pronounced dead and a new player is born.
  641.  
  642. void HandlePlayerFalling (void)
  643. {
  644.     if (thePlayer.hVel > 0)                // Handle horizontal air resistance.
  645.     {
  646.         thePlayer.hVel -= kAirResistance;
  647.         if (thePlayer.hVel < 0)
  648.             thePlayer.hVel = 0;
  649.     }
  650.     else if (thePlayer.hVel < 0)
  651.     {
  652.         thePlayer.hVel += kAirResistance;
  653.         if (thePlayer.hVel > 0)
  654.             thePlayer.hVel = 0;
  655.     }
  656.     
  657.     thePlayer.vVel += kGravity;            // Add in effect of gravity.
  658.     
  659.     if (thePlayer.vVel > kMaxVVelocity)    // Keep player from falling too fast.
  660.         thePlayer.vVel = kMaxVVelocity;
  661.     else if (thePlayer.vVel < -kMaxVVelocity)
  662.         thePlayer.vVel = -kMaxVVelocity;
  663.     
  664.     thePlayer.h += thePlayer.hVel;        // Move player's x and y (h and v)…
  665.     thePlayer.v += thePlayer.vVel;        // by amount of velocity in each direction.
  666.     
  667.     SetAndCheckPlayerDest();            // Check for wrap-around, etc.
  668.     
  669.     CheckLavaRoofCollision();            // See if they hit roof or lava.
  670.     CheckPlatformCollision();            // See if they crashed to a ledge.
  671. }
  672.  
  673. //--------------------------------------------------------------  HandlePlayerBones
  674.  
  675. // This is when the player is just a static pile of bones on a platform.  They…
  676. // have been killed by an enemy and now are waiting to slip away so that a new…
  677. // player can be born.
  678.  
  679. void HandlePlayerBones (void)
  680. {
  681.     if (evenFrame)                    // To slow it down a bit, action only occurs…
  682.     {                                // on the even frames.
  683.         thePlayer.frame--;            // Drop the counter down by one.
  684.         if (thePlayer.frame == 0)    // When counter reaches zero, player officially dies.
  685.             OffAMortal();
  686.         else                        // Otherwise, player's bones are sinking.
  687.             thePlayer.dest.top = thePlayer.dest.bottom - thePlayer.frame;
  688.     }
  689. }
  690.  
  691. //--------------------------------------------------------------  MovePlayer
  692.  
  693. // This function is the sort of "master movement" function.  It looks…
  694. // at what mode a player is in and calls the appropriate function from…
  695. // above.  Arcade games (at least this one) tend to be very "modal" in…
  696. // this way.  It's the actions of the user and the enemies in the game…
  697. // that cause the player's mode to move from one state to another.
  698.  
  699. void MovePlayer (void)
  700. {
  701.     switch (thePlayer.mode)        // Check the "mode" the player is in.
  702.     {
  703.         case kIdle:                // Invulnerable - standing there - just born.
  704.         HandlePlayerIdle();
  705.         break;
  706.         
  707.         case kFlying:            // Flapping, floating, airborne.
  708.         HandlePlayerFlying();
  709.         break;
  710.         
  711.         case kWalking:            // On terra firma.  Standing or walking on ledge.
  712.         HandlePlayerWalking();
  713.         break;
  714.         
  715.         case kSinking:            // Trapped in the lava - going down.
  716.         HandlePlayerSinking();
  717.         break;
  718.         
  719.         case kFalling:            // Dead - a skeleton falling to earth.
  720.         HandlePlayerFalling();
  721.         break;
  722.         
  723.         case kBones:            // Dead - a static pile of bones on a ledge.
  724.         HandlePlayerBones();
  725.         break;
  726.     }
  727. }
  728.  
  729. //--------------------------------------------------------------  CheckTouchDownCollision
  730.  
  731. // This function determines whether or not the player is landed on a ledge.
  732. // It does this by doing a rectangle collision between the player's bounding…
  733. // rectangle and an imaginary rectangle enclosing an area above the ledges.
  734. // I call these imaginary rectangles "touchDownRects[]".  The trick was that…
  735. // you don't want the player to have to "hit" the top of a ledge in order to…
  736. // land on it - there is an arbitrary distance above a ledge where, if the player…
  737. // is within this area, the legs ought to come out and the player flagged as…
  738. // walking.  As well, this same function is used for a walking player to see…
  739. // if they are still on the ledge (they may walk off the edge).
  740.  
  741. void CheckTouchDownCollision (void)
  742. {
  743.     Rect        testRect, whoCares;
  744.     short        i, offset;
  745.     Boolean        sected;
  746.     
  747.     sected = FALSE;                                // Assume not on ledge.
  748.     for (i = 0; i < numLedges; i++)                // Go through all ledges.
  749.     {
  750.         testRect = touchDownRects[i];            // Here's the imaginary rect.
  751.         if (thePlayer.mode == kWalking)            // We need an offset if player walking…
  752.             OffsetRect(&testRect, 0, 11);        // since the player graphic is taller.
  753.         
  754.         if (SectRect(&thePlayer.dest, &testRect, &whoCares))
  755.         {                                        // Does the player's rect intersect?
  756.             if (thePlayer.mode == kFlying)        // Okay, it does, is the player airborne?
  757.             {
  758.                 thePlayer.mode = kWalking;        // Put player into walking mode.
  759.                 if (thePlayer.facingRight)        // Assign correct graphic for player.
  760.                     thePlayer.srcNum = 5;
  761.                 else
  762.                     thePlayer.srcNum = 6;
  763.                 if (thePlayer.vVel > 0)            // Stop player from falling further.
  764.                     thePlayer.vVel = 0;
  765.                 thePlayer.dest.bottom += 11;    // "Grow" player's bounding rect.
  766.                 thePlayer.wasDest.bottom += 11;
  767.                                                 // Move player so standing on top of ledge.
  768.                 offset = thePlayer.dest.bottom - testRect.bottom - 1;
  769.                 thePlayer.dest.bottom -= offset;
  770.                 thePlayer.dest.top -= offset;
  771.                 thePlayer.v = thePlayer.dest.top << 4;
  772.                                                 // Play brief collision sound.
  773.                 PlayExternalSound(kGrateSound, kGratePriority);
  774.             }
  775.             sected = TRUE;                        // Make note that we've landed.
  776.         }
  777.     }
  778.     
  779.     if (!sected)                                // Now, if we didn't collide…
  780.     {                                            // were we walking?
  781.         if (thePlayer.mode == kWalking)            // Did we walk off the ledge?
  782.         {
  783.             thePlayer.mode = kFlying;            // Set player to flying mode.
  784.             thePlayer.dest.bottom -= 11;        // Resize player's bounding rect.
  785.             thePlayer.wasDest.bottom -= 11;
  786.         }
  787.     }
  788. }
  789.  
  790. //--------------------------------------------------------------  CheckPlatformCollision
  791.  
  792. // Unlike the above function, this one tests the player's bounding rect against…
  793. // the bounding rect of each ledge (not an imaginary rect above the ledge).  This…
  794. // function is primarily for (then) collisions off the bottom and sides of the…
  795. // ledges.  In this way, the ledges are "solid" - not able to be passed through.
  796.  
  797. void CheckPlatformCollision (void)
  798. {
  799.     Rect        hRect, vRect, whoCares;
  800.     short        i, offset;
  801.     
  802.     for (i = 0; i < numLedges; i++)                    // Walk through all ledges.
  803.     {                                                // Test rectangle overlap.
  804.         if (SectRect(&thePlayer.dest, &platformRects[i], &whoCares))
  805.         {                                            // If player intersecting ledge…
  806.             hRect.left = thePlayer.dest.left;        // Create our special test rect.
  807.             hRect.right = thePlayer.dest.right;
  808.             hRect.top = thePlayer.wasDest.top;
  809.             hRect.bottom = thePlayer.wasDest.bottom;
  810.                                                     // Determine if the player hit the…
  811.                                                     // top/bottom of the ledge or the…
  812.                                                     // sides of the ledge.
  813.             if (SectRect(&hRect, &platformRects[i], &whoCares))
  814.             {                                        // We're fairly sure the player hit…
  815.                                                     // the left or right edge of ledge.
  816.                 if (thePlayer.h > thePlayer.wasH)    // If player was heading right…
  817.                 {                                    // player will bounce to left.
  818.                     offset = thePlayer.dest.right - platformRects[i].left;
  819.                     thePlayer.dest.left -= offset;
  820.                     thePlayer.dest.right -= offset;
  821.                     thePlayer.h = thePlayer.dest.left << 4;
  822.                     if (thePlayer.hVel > 0)            // We bounce back with 1/2 our vel.
  823.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  824.                     else
  825.                         thePlayer.hVel = thePlayer.hVel >> 1;
  826.                 }                                    // Else if player was heading left…
  827.                 else if (thePlayer.h < thePlayer.wasH)
  828.                 {                                    // player will bounce right.
  829.                     offset = platformRects[i].right - thePlayer.dest.left;
  830.                     thePlayer.dest.left += offset;
  831.                     thePlayer.dest.right += offset;
  832.                     thePlayer.h = thePlayer.dest.left << 4;
  833.                     if (thePlayer.hVel < 0)            // We bounce back with 1/2 our vel.
  834.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  835.                     else
  836.                         thePlayer.hVel = thePlayer.hVel >> 1;
  837.                 }                                    // Play impact sound.
  838.                 PlayExternalSound(kGrateSound, kGratePriority);
  839.             }
  840.             else                                    // It doesn't look like we hit the…
  841.             {                                        // the left or right edge of ledge.
  842.                 vRect.left = thePlayer.wasDest.left;
  843.                 vRect.right = thePlayer.wasDest.right;
  844.                 vRect.top = thePlayer.dest.top;
  845.                 vRect.bottom = thePlayer.dest.bottom;
  846.                                                     // So we'll test top/bottom collision.
  847.                 if (SectRect(&vRect, &platformRects[i], &whoCares))
  848.                 {                                    // We've decided we've hit top/bottom.
  849.                     if (thePlayer.wasV < thePlayer.v)
  850.                     {                                // If we were heading down (hit top)…
  851.                                                     // keep player on top of ledge.
  852.                         offset = thePlayer.dest.bottom - platformRects[i].top;
  853.                         thePlayer.dest.top -= offset;
  854.                         thePlayer.dest.bottom -= offset;
  855.                         thePlayer.v = thePlayer.dest.top << 4;
  856.                                                     // Play collision sound.
  857.                         if (thePlayer.vVel > kDontFlapVel)
  858.                             PlayExternalSound(kGrateSound, kGratePriority);
  859.                                                     // If we were falling bones (dead)…
  860.                         if (thePlayer.mode == kFalling)
  861.                         {                            // we'll bounce.
  862.                             if ((thePlayer.dest.right - 16) > platformRects[i].right)                            {
  863.                                 thePlayer.hVel = 16;
  864.                                 if (thePlayer.vVel > 0)
  865.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  866.                                 else
  867.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  868.                             }
  869.                             else if ((thePlayer.dest.left + 16) < platformRects[i].left)
  870.                             {
  871.                                 thePlayer.hVel = -16;
  872.                                 if (thePlayer.vVel > 0)
  873.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  874.                                 else
  875.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  876.                             }
  877.                             else                    // If we were nearly stopped…
  878.                             {                        // turn into pile of bones.
  879.                                 PlayExternalSound(kBoom1Sound, kBoom1Priority);
  880.                                 thePlayer.vVel = 0;
  881.                                 thePlayer.mode = kBones;
  882.                                 thePlayer.frame = 22;
  883.                                 thePlayer.dest.top = thePlayer.dest.bottom - 22;
  884.                                 thePlayer.v = thePlayer.dest.top << 4;
  885.                                 thePlayer.srcNum = 10;
  886.                             }
  887.                         }
  888.                         else                        // Okay, if we weren't falling bones…
  889.                         {                            // bounce the player (-1/2 vel.).
  890.                             if (thePlayer.vVel > 0)
  891.                                 thePlayer.vVel = -(thePlayer.vVel >> 1);
  892.                             else
  893.                                 thePlayer.vVel = thePlayer.vVel >> 1;
  894.                         }
  895.                     }                                // If the player was instead moving up…
  896.                     else if (thePlayer.wasV > thePlayer.v)
  897.                     {                                // the player likely hit the bottom of…
  898.                                                     // the ledge.  Keep player below ledge.
  899.                         offset = platformRects[i].bottom - thePlayer.dest.top;
  900.                         thePlayer.dest.top += offset;
  901.                         thePlayer.dest.bottom += offset;
  902.                         thePlayer.v = thePlayer.dest.top << 4;
  903.                                                     // Play collision sound.
  904.                         PlayExternalSound(kGrateSound, kGratePriority);
  905.                         if (thePlayer.vVel < 0)        // Bounce player down (-1/2 vel.).
  906.                             thePlayer.vVel = -(thePlayer.vVel >> 1);
  907.                         else
  908.                             thePlayer.vVel = thePlayer.vVel >> 1;
  909.                     }
  910.                 }
  911.             }
  912.         }
  913.     }
  914. }
  915.  
  916. //--------------------------------------------------------------  KeepPlayerOnPlatform
  917.  
  918. // This is an alignment function.  It is called only if the player is standing or…
  919. // walking on a ledge.  It is designed to keep the player's mount's (bird's)…
  920. // feet firmly planted on the ledge.  Consider that, with the addition of gravity…
  921. // to a player's downward velocity, there is a problem where the player can appear…
  922. // to slowly sink down through the ledge.  There may be any number of methods you…
  923. // might want to try to prevent this from becoming a problem in the first place, …
  924. // but my experience has been that all the methods I've tried have flaws - correcting…
  925. // for those flaws points out other flaws and you start getting a messy sort of…
  926. // patchwork.  Should you ever get it to work, the mess that is your function has come…
  927. // to resemble the Knot of ????.
  928.  
  929. void KeepPlayerOnPlatform (void)
  930. {
  931.     Rect        whoCares;
  932.     short        i, offset;
  933.     
  934.     for (i = 0; i < numLedges; i++)        // For each ledge for this wave…
  935.     {                                    // test for a collision.
  936.         if ((SectRect(&thePlayer.dest, &platformRects[i], &whoCares)) && (thePlayer.vVel > 0))
  937.         {                                // If collided (player sinking), force…
  938.                                         // player to top of ledge.
  939.             offset = thePlayer.dest.bottom - platformRects[i].top - 1;
  940.             thePlayer.dest.top -= offset;
  941.             thePlayer.dest.bottom -= offset;
  942.             thePlayer.v = thePlayer.dest.top * 16;
  943.         }
  944.     }
  945.     
  946.     if (thePlayer.vVel > 0)                // Set player's vertical velocity to zero.
  947.         thePlayer.vVel = 0;
  948. }
  949.  
  950. //--------------------------------------------------------------  CheckLavaRoofCollision
  951.  
  952. // This is a simple high/low test to see if the player has either bounced off…
  953. // the roof of the "arena" or dipped down into the lava below.
  954.  
  955. void CheckLavaRoofCollision (void)
  956. {
  957.     short        offset;
  958.     
  959.     if (thePlayer.dest.bottom > kLavaHeight)    // See if player in lava.
  960.     {
  961.         if (thePlayer.mode == kFalling)            // If falling (dead), "Splash!"
  962.             PlayExternalSound(kSplashSound, kSplashPriority);
  963.         else                                    // If flying (alive), "Yeow!"
  964.             PlayExternalSound(kBirdSound, kBirdPriority);
  965.         thePlayer.mode = kSinking;                // Irregardless, player is now sinking.
  966.     }
  967.     else if (thePlayer.dest.top < kRoofHeight)    // See if player hit roof.
  968.     {                                            // Move player to below roof.
  969.         offset = kRoofHeight - thePlayer.dest.top;
  970.         thePlayer.dest.top += offset;
  971.         thePlayer.dest.bottom += offset;
  972.         thePlayer.v = thePlayer.dest.top * 16;
  973.                                                 // Play collision sound.
  974.         PlayExternalSound(kGrateSound, kGratePriority);
  975.         thePlayer.vVel = thePlayer.vVel / -2;    // Rebound player (-1/2 vel.).
  976.     }
  977. }
  978.  
  979. //--------------------------------------------------------------  SetAndCheckPlayerDest
  980.  
  981. // This function keeps our player's screen coordinates and "scaled" coordinates…
  982. // in agreement.  As well, it checks for wrap-around and handles it.
  983.  
  984. void SetAndCheckPlayerDest (void)
  985. {
  986.     short        wasTall, wasWide;
  987.                                         // Remember width and height of player.
  988.     wasTall = thePlayer.dest.bottom - thePlayer.dest.top;
  989.     wasWide = thePlayer.dest.right - thePlayer.dest.left;
  990.                                         // Convert scaled coords to screen coords.
  991.     thePlayer.dest.left = thePlayer.h >> 4;
  992.     thePlayer.dest.right = thePlayer.dest.left + wasWide;
  993.     thePlayer.dest.top = thePlayer.v >> 4;
  994.     thePlayer.dest.bottom = thePlayer.dest.top + wasTall;
  995.     
  996.     if (thePlayer.dest.left > 640)        // Has player left right side of arena?
  997.     {                                    // Wrap player back to left side of screen.
  998.         OffsetRect(&thePlayer.dest, -640, 0);
  999.         thePlayer.h = thePlayer.dest.left << 4;
  1000.         OffsetRect(&thePlayer.wasDest, -640, 0);
  1001.     }
  1002.     else if (thePlayer.dest.right < 0)    // Else, has player left left side of screen?
  1003.     {                                    // Wrap player around to right side of screen.
  1004.         OffsetRect(&thePlayer.dest, 640, 0);
  1005.         thePlayer.h = thePlayer.dest.left << 4;
  1006.         OffsetRect(&thePlayer.wasDest, 640, 0);
  1007.     }
  1008. }
  1009.  
  1010. //--------------------------------------------------------------  HandleLightning
  1011.  
  1012. // Lightning is handled here.  Obelisks are flashed, lightning is generated, …
  1013. // lighting strikes, and the lightning counter decremented.  This is pretty…
  1014. // nice - we can just set "lightningCount" to a non-zero number and this…
  1015. // function will strike lightning every fram until the counter returns to zero.
  1016.  
  1017. void HandleLightning (void)
  1018. {
  1019.     if (lightningCount > 0)                        // Is lightning to strik this frame?
  1020.     {                                            // Special frame when obelisks are lit.
  1021.         if (lightningCount == kNumLightningStrikes)
  1022.             FlashObelisks(TRUE);
  1023.         GenerateLightning(lightH, lightV);        // Create new lightning "segments".
  1024.         StrikeLightning();                        // Draw lightning on screen.
  1025.     }
  1026. }
  1027.  
  1028. //--------------------------------------------------------------  FinishLightning
  1029.  
  1030. // This undoes what the lightning did.  It "undraws" the lightning and returns…
  1031. // the obelisks to their "non lit" state.  I see that it is HERE where the counter…
  1032. // is decremented and not in the function above.
  1033.  
  1034. void FinishLightning (void)
  1035. {
  1036.     if (lightningCount > 0)
  1037.     {
  1038.         StrikeLightning();                        // Undraw lightning (exclusive Or).
  1039.         lightningCount--;                        // Descrement lightning counter.
  1040.         if (lightningCount == 0)                // If this is the last lightning strike…
  1041.             FlashObelisks(FALSE);                // return obelisk to normal.
  1042.                                                 // "BOOOOM!"
  1043.         PlayExternalSound(kLightningSound, kLightningPriority);
  1044.     }
  1045. }
  1046.  
  1047. //--------------------------------------------------------------  HandleCountDownTimer
  1048.  
  1049. // This is a pretty boring function.  It is here so that when one level ends,…
  1050. // the next one does begin immediately.  It gives the player a few seconds of…
  1051. // breathing time.  Essentially, to engage it, we need merely set "countDownTimer"…
  1052. // to a positive number.  Each frame the counter gets decremented.  When it…
  1053. // reaches zero, the level is advanced to the next wave.
  1054.  
  1055. void HandleCountDownTimer (void)
  1056. {
  1057.     if (countDownTimer == 0)        // If already zero, do nothing.
  1058.         return;
  1059.     else                            // Otherwise, if greater than zero…
  1060.     {
  1061.         countDownTimer--;            // decrememnt counter.
  1062.         if (countDownTimer == 0)    // Did it just hit zero?
  1063.         {
  1064.             countDownTimer = 0;        // Well, just to be sure (dumb line of code).
  1065.             levelOn++;                // Increment the level (wave) we're on.
  1066.             UpdateLevelNumbers();    // Display new level on screen.
  1067.             SetUpLevel();            // Set up the platforms.
  1068.             GenerateEnemies();        // Ready nemesis.
  1069.         }
  1070.     }
  1071. }
  1072.  
  1073. //--------------------------------------------------------------  PlayGame
  1074.  
  1075. // Here is the "core" of the "game loop".  When a player has elected to…
  1076. // begin a game, Glypha falls into this function and remains in a loop…
  1077. // herein until the player either quits, or loses their last "bird".
  1078. // Each pass through the main loop below constitutes one "frame" of the game.
  1079.  
  1080. void PlayGame (void)
  1081. {
  1082.     #define        kTicksPerFrame        2L
  1083.     Point        offsetPt;
  1084.     long        waitUntil;
  1085.     
  1086.     offsetPt.h = 0;                                // Set up ShieldCursor() point.
  1087.     offsetPt.v = 20;
  1088.     ShieldCursor(&mainWindowRect, offsetPt);    // Hide the cursor. 
  1089.     waitUntil = TickCount() + kTicksPerFrame;    // Set up speed governor variable.
  1090.     
  1091.     do                                            // Main game loop!!!!
  1092.     {    
  1093.         MovePlayer();                            // Move the player's bird.
  1094.         MoveEnemies();                            // Move all sphinx enemies.
  1095.         HandleHand();                            // Handle the mummy hand (may do nothing).
  1096.         HandleEye();                            // Handle eye (probably will do nothing).
  1097.         DrawFrame();                            // Draw the whole scene for this frame.
  1098.         HandleLightning();                        // Draw lightning (is applicable).
  1099.         do                                        // Here is where the speed is governed.
  1100.         {
  1101.         } while (TickCount() < waitUntil);
  1102.         waitUntil = TickCount() + kTicksPerFrame;
  1103.         evenFrame = !evenFrame;                    // Toggle "evenFrame" variable.
  1104.         
  1105.         GetPlayerInput();                        // Get the player's input (keystrokes).
  1106.         HandleCountDownTimer();                    // Handle countdown (may do nothing).
  1107.         FinishLightning();                        // Undraw lightning (if it needs undoing).
  1108.     }
  1109.     while ((playing) && (!pausing));            // Stay in loop until dead, paused or quit.
  1110.     
  1111.     if ((!playing) && (!quitting))                // If the player died!
  1112.     {                                            // Then play some sweet music.
  1113.         PlayExternalSound(kMusicSound, kMusicPriority);
  1114.         CheckHighScore();                        // And see if they're on the high scores.
  1115.     }
  1116.     
  1117.     ShowCursor();                                // Before we go, restore the cursor.
  1118.     MenusReflectMode();                            // Set the menus grayed-out state correctly.
  1119.     FlushEvents(everyEvent, 0);                    // Flush any events in the queue.
  1120. }
  1121.  
  1122. //--------------------------------------------------------------  CheckHighScore
  1123.  
  1124. // This function handles testing to see if the player's score is in the …
  1125. // high scores.  If that is the case, the function prompts the user for…
  1126. // a name to enter, and sorts and stores off the new score list.
  1127.  
  1128. void CheckHighScore (void)
  1129. {
  1130.     #define        kHighNameDialogID    130
  1131.     Str255        placeStr, tempStr;
  1132.     DialogPtr    theDial;
  1133.     short        i, item;
  1134.     Boolean        leaving;
  1135.     
  1136.     if (theScore > thePrefs.highScores[9])        // To see if on high scores, we need…
  1137.     {                                            // merely see if the last guy is beat out.
  1138.         openTheScores = TRUE;                    // Will automatically bring up high scores.
  1139.                                                 // Play some congratulatory music.
  1140.         PlayExternalSound(kBonusSound, kMusicPriority - 1);
  1141.         i = 8;                                    // Find where new score fits in list.
  1142.         while ((theScore > thePrefs.highScores[i]) && (i >= 0))
  1143.         {                                        // We'll bump everyone down as we look.
  1144.             thePrefs.highScores[i + 1] = thePrefs.highScores[i];
  1145.             thePrefs.highLevel[i + 1] = thePrefs.highLevel[i];
  1146.             PasStringCopy(thePrefs.highNames[i], thePrefs.highNames[i + 1]);
  1147.             i--;
  1148.         }
  1149.         
  1150.         i++;                                    // i is our place in list (zero based).
  1151.         thePrefs.highScores[i] = theScore;        // Pop the new score in place.
  1152.         thePrefs.highLevel[i] = levelOn + 1;    // Drop in the new highest level.
  1153.         
  1154.         NumToString((long)i + 1L, placeStr);    // Convert place to a string to display…
  1155.         ParamText(placeStr, "\p", "\p", "\p");    // in the dialog (via ParamText()).
  1156.         
  1157.         InitCursor();                            // Show cursor.
  1158.         CenterDialog(kHighNameDialogID);        // Center the dialog and then bring it up.
  1159.         theDial = GetNewDialog(kHighNameDialogID, 0L, kPutInFront);
  1160.         SetPort((GrafPtr)theDial);
  1161.         ShowWindow((GrafPtr)theDial);            // Make dialog visible.
  1162.         DrawDefaultButton(theDial);                // Draw outline around "Okay" button.
  1163.         FlushEvents(everyEvent, 0);                // Flush any events queued up.
  1164.                                                 // Put a default name in text edit box.
  1165.         SetDialogString(theDial, 2, thePrefs.highName);
  1166.         SelIText(theDial, 2, 0, 1024);            // Select the whole text edit string.
  1167.         leaving = FALSE;                        // Flag for noting when player hit "Okay".
  1168.         
  1169.         while (!leaving)                        // Simple modal dialog loop.
  1170.         {
  1171.             ModalDialog(0L, &item);                // Use standard filtering.
  1172.             
  1173.             if (item == 1)                        // If player hit the "Okay" button…
  1174.             {                                    // Get the name entered in text edit box.
  1175.                 GetDialogString(theDial, 2, tempStr);
  1176.                                                 // Copy the name into high score list.
  1177.                 PasStringCopyNum(tempStr, thePrefs.highNames[i], 15);
  1178.                 PasStringCopy(thePrefs.highNames[i], thePrefs.highName);
  1179.                 leaving = TRUE;                    // We're gone!
  1180.             }
  1181.         }
  1182.         
  1183.         DisposDialog(theDial);                    // Clean up.
  1184.     }
  1185.     else                                        // But if player didn't get on high scores…
  1186.         openTheScores = FALSE;                    // no need to rub their face in it.
  1187. }
  1188.  
  1189.